#pragma once

/**
 * @file HttpContext.hpp
 * @author 1102403684@qq.com
 * @brief
 * @version 0.1
 * @date 2024-10-30
 *
 * @copyright Copyright (c) 2024
 *
 */
#include "../common/logger.hpp"
#include "../src/Buffer.hpp"
#include "HttpRequest.hpp"
#include "Util.hpp"

#define MAX_LINE 8196 /*8kb*/

enum class HttpRecvStatus
{
    RECV_ERROR,
    RECV_LINE,
    RECV_HEAD,
    RECV_BODY,
    RECV_OVER
};

class HttpContext
{
public:
    HttpContext() : resp_statu_(200), recv_statu_(HttpRecvStatus::RECV_LINE)
    {
    }

    int get_resp_statu()
    {
        return resp_statu_;
    }
    HttpRecvStatus get_recv_statu()
    {
        return recv_statu_;
    }


    HttpRequest &get_request()
    {
        return request_;
    }

    /**
     * @brief 接收并解析Http请求
     * 
     */
    void recv_and_parse(Buffer* buffer)
    {
        switch(recv_statu_){
            case HttpRecvStatus::RECV_LINE:
                recv_line(buffer);
            case HttpRecvStatus::RECV_HEAD:
                recv_head(buffer);
            case HttpRecvStatus::RECV_BODY:
                recv_body(buffer);
        }
    }

    void reset()
    {
        recv_statu_ = HttpRecvStatus::RECV_LINE;
        resp_statu_ = 200;
        request_.clear();
    }

private:
    /**
     * @brief
     * @details
     * - 在URL的path部分，空格会被编码为%20，而不会转化为加号，所以在解码时，不需要将+变为空格
     * - 在URL的query参数中，由RFC3986，空格有时可能会被编码为加号，所以需要考虑加号将其转为空格
     * - (GET|PUT|DELETE|POST|HEAD) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?
     *   - (GET|PUT|DELETE|POST|HEAD)，| 表示或，()表示匹配组
     *   - ([^?]*)，匹配任意多个非？字符
     *   - (?:\\?(.*))? ，(?:xxx)表示非捕获组，?(.*)这样就做到了不捕获?而捕捉查询字符串的目的，最后的？表示可以没有查询字符串
     *   - (HTTP/1\\.[01]) ， [01] 表示匹配0或1
     *   - (?:\n|\r\n)? ，表示匹配linux/windows风格的换行
     * @param line
     * @return true
     * @return false
     */
    bool parse_line(const std::string &line)
    {
        // 正则表达式进行解析
        std::regex re{"(GET|PUT|DELETE|POST|HEAD) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase};
        std::smatch matches;

        //
        int ret = std::regex_match(line, matches, re);
        if (!ret)
        {
            LOG_DEBUG("解析请求行失败");
            recv_statu_ = HttpRecvStatus::RECV_ERROR;
            resp_statu_ = 400; /*bad request*/
            return false;
        }

        request_.set_method(matches[1]);
        //保证将get等转化为 GET 
        std::transform(request_.method_.begin(), request_.method_.end(), request_.method_.begin(), ::toupper);
        request_.set_path(Utils::url_decode(matches[2], false)); //   /hello/c++11
        request_.set_version(matches[4]);
        std::vector<std::string> query_string_arry;
        std::string query_string = matches[3];
        Utils::spilt(query_string, "&", &query_string_arry);
        for (auto str : query_string_arry)
        {
            size_t pos = str.find("=");
            if (pos == std::string::npos)
            {
                LOG_DEBUG("查询参数有误");
                recv_statu_ = HttpRecvStatus::RECV_ERROR;
                resp_statu_ = 400; /*bad request*/
                return false;
            }
            std::string key = Utils::url_decode(str.substr(0, pos), true);
            std::string val = Utils::url_decode(str.substr(pos + 1), true); // 查询字符串中，空格会被转换为加号，所以要将他转换回空格
            request_.set_param(key, val);
        }
        return true;
    }

    /**
     * @brief 读取请求行并解析
     *
     * @param buffer
     * @return true
     * @return false
     */
    bool recv_line(Buffer *buffer)
    {
        if (recv_statu_ != HttpRecvStatus::RECV_LINE)
            return false;
        // 1 获取一行数据(没有一行或一行很大)
        std::string line = buffer->get_line_pop();
        if (line.size() == 0)
        {
            // 如果很长却没有换行
            if (buffer->get_readable_size() > MAX_LINE)
            {
                recv_statu_ = HttpRecvStatus::RECV_ERROR;
                resp_statu_ = 414; /*uri too long*/
                return false;
            }
            // 否则等待新数据
            return true;
        }

        // 如果过于长
        if (line.size() > MAX_LINE)
        {
            recv_statu_ = HttpRecvStatus::RECV_ERROR;
            resp_statu_ = 414; /*uri too long*/
            return false;
        }

        bool ret = parse_line(line);
        if (!ret)
        {
            return false;
        }

        // 首行处理结束，开始进入头部获取阶段
        recv_statu_ = HttpRecvStatus::RECV_HEAD;
        return true;
    }

    bool parse_head(const std::string &line)
    {
        size_t pos = line.find(":");
        if (pos == std::string::npos)
        {
            recv_statu_ = HttpRecvStatus::RECV_ERROR;
            resp_statu_ = 400; /*bad request*/
            return false;
        }

        std::string key = Utils::trim(line.substr(0, pos));
        std::string val = Utils::trim(line.substr(pos + 1));
        request_.set_header(key, val);
        return true;
    }

    /**
     * @brief 一行行取出数据并解析 直到遇到空行为止
     *
     * @param buffer
     */
    bool recv_head(Buffer *buffer)
    {
        if (recv_statu_ != HttpRecvStatus::RECV_HEAD)
            return false;

        while (true)
        {
            std::string line = buffer->get_line_pop();
            if (line.size() == 0)
            {
                if (buffer->get_readable_size() > MAX_LINE)
                {
                    recv_statu_ = HttpRecvStatus::RECV_ERROR;
                    resp_statu_ = 414; /*uri too long*/
                    return false;
                }
                return true;
            }
            if (line.size() > MAX_LINE)
            {
                recv_statu_ = HttpRecvStatus::RECV_ERROR;
                resp_statu_ = 414; /*uri too long*/
                return false;
            }

            if (line == "\n" || line == "\r\n")
                break;

            bool ret = parse_head(line);
            if (!ret)
            {
                return false;
            }
        }
        // headers处理结束，开始进入正文获取阶段
        recv_statu_ = HttpRecvStatus::RECV_BODY;
        return true;
    }


    bool recv_body(Buffer *buffer)
    {
        if (recv_statu_ != HttpRecvStatus::RECV_BODY)
            return false;

        // 1.获取正文长度
        size_t length = request_.get_content_length();
        if (length == 0)
        {
            // 没有正文 请求处理完毕
            recv_statu_ = HttpRecvStatus::RECV_OVER;
            return true;
        }
        // 2.当前已经接受了多少正文request.body().size()
        size_t real_length = length - request_.get_body().size(); // 还需要接收的正文长度
        // 3.接收正文放到body中，需要考虑当前缓冲区中的数据全是正文
        //  3.1 缓冲区的数据，包含了请求所有的数据
        if (buffer->get_readable_size() >= real_length)
        {
            //这里是可以优化的，追加的时候可以不用构造一个新的对象
            request_.append_body(buffer->get_reader_addr(), real_length);
            buffer->move_read_offset(real_length);
            recv_statu_ = HttpRecvStatus::RECV_OVER;
            return true;
        }
        // 3.2 缓冲区的数据，无法装下正文的需要
        request_.append_body(buffer->get_reader_addr(), buffer->get_readable_size());
        buffer->move_read_offset(buffer->get_readable_size());
        recv_statu_ = HttpRecvStatus::RECV_BODY;
        return true;
    }

private:
    int resp_statu_;
    HttpRecvStatus recv_statu_;
    HttpRequest request_;
};